///////////////////////////////////////////////////////////////////////////////////////////////////
// Illusion Ray Development Kit (IDK)           Copyright (c) 2010 ( dimitriy.martynenko@gmail.com )
///////////////////////////////////////////////////////////////////////////////////////////////////
// Created : 2010-05-22
// Updated : ...
// Licence : NEW BSD
// Info	   : "SCL" Safe Code Lib (for C++ unmanaged)
///////////////////////////////////////////////////////////////////////////////////////////////////
/*-------------------------------------------------------------------------
| lock:
| ------------
| Keyword for lock 
---------------------------------------------------------------------------*/
#define lock(x) Threading::ExThreadLock __lock(x)

#define SLEEP_TIME_ITERATION 1000
#define SLEEP_TIME 1

namespace Threading
{
#include <Timer.hpp>

	typedef ref<THandleBindPtr<Platform::HANDLE>> ThreadHandle;

	class TThreadObject : public TObject
	{
	private:
		Platform::LONG mThreadID;
	public:
		inline TThreadObject() : TObject(), mThreadID(0) {}

		volatile inline bool Lock(void)
		{
			Platform::LONG temp = Platform::InterlockedCompareExchange(&mThreadID, Platform::GetCurrentThreadId(), 0);
			return temp != 0;
		}

		volatile inline void UnLock(void)
		{
			Platform::InterlockedExchange(&mThreadID, 0);
		}
	};

	class ExThreadLock
	{
	private:
		ref<TThreadObject> mMutex;
	public:
		inline ExThreadLock(ref<TThreadObject> mutex) : mMutex(mutex)
		{
			Locked();
		}

		~ExThreadLock()
		{ 
			Unlocked();
		}

		inline void Locked()
		{
			for(int i = 0; mMutex->Lock(); ++i)
			{
				if(i == SLEEP_TIME_ITERATION)
				{
					i = 0;
					Platform::Sleep(SLEEP_TIME);
				}
			}
		}

		inline void Unlocked()
		{
			mMutex->UnLock();
		}
	};

	struct IThreadEvent : virtual public IObject
	{
		virtual void ReadyForProcessing(void) = 0;
	};

	class TThread : public TThreadObject, virtual public IThreadEvent
	{
		InterfaceInClass;
	public:
	private:
		Platform::HANDLE mReadyForProcessing;
		Platform::HANDLE mReadyToSync;
		Platform::HANDLE mWaitToSync;
		Platform::DWORD mThreadID;
		HandlePtr mThreadContext;
		Time::Timer mTimer;
		bool mThreadStop;
	protected:
		inline static Platform::DWORD WINAPI __ThreadCall(void * p)
		{
			if(!p) return 0;

			{
				iLog("__Thread...begin");
				TThread* threadclass = (TThread*) p;
				threadclass->OnThreadStart();
				while(threadclass->OnThreadUpdate(threadclass->GetLastUpdateDeltaTime(), true))
				{
					//empty
				}
				iLog("__Thread...end");
			}
			return 0;
		}
		inline void ReadyToSync(void)
		{
			lock(this);
			Platform::ResetEvent(mReadyForProcessing);
			Platform::SetEvent(mReadyToSync);
		}
	public:
		inline TThread() 
			: mThreadID(0),
			mTimer(new Time::Timer::Create()), 
			mThreadStop(false)
		{}

		inline void ThreadStart(void)
		{
			mReadyForProcessing = Platform::CreateEventA(0, 0, 0, 0);
			mReadyToSync = Platform::CreateEventA(0, 0, 0, 0);
			mWaitToSync = Platform::CreateEventA(0, 0, 0, 0);
			//
			mThreadContext = new ThreadHandle::Create(Platform::CreateThread(Null, 0, __ThreadCall, this, 0, &mThreadID));

			Platform::SetEvent(mReadyForProcessing);	
		}

		inline bool IsThreadStart(void)
		{
			return mThreadContext != NullPtr;
		}

		inline void ThreadSyncExit(void)
		{
			//lock(this);

			mThreadStop = true;

			iLog("wait...ThreadSyncExit");

			ReadyToSync();

			//Wait call back
			if(WAIT_OBJECT_0 != Platform::WaitForSingleObject(mWaitToSync, INFINITE))
			{
				iLog("Sync waiting error");
			}
			iLog("exit...ThreadSyncExit");
			//Clear handle
			mThreadContext = NullPtr;
		}

		inline void ThreadLock(void)
		{
			//lock(this);

			iLog("lock wait...thread");

			ReadyToSync();

			//Wait call back
			if(WAIT_OBJECT_0 != Platform::WaitForSingleObject(mWaitToSync, INFINITE))
			{
				iLog("Sync waiting error");
			}

			iLog("lock...thread");
		}

		inline void ThreadUnlock(void)
		{
			iLog("unlock...thread");

			ReadyForProcessing();
		}
	public:
		virtual void ReadyForProcessing(void)
		{
			lock(this);
			Platform::SetEvent(mReadyForProcessing);
		}
	public:
		/**/
		virtual void OnThreadStart(void)
		{
			iLog("Thread Run");
			mTimer->Start();
			//Platform::SetThreadAffinityMask(Platform::GetCurrentThread(), mMode);
		}

		virtual void OnThreadStop(void)
		{
			iLog("Thread Stoped");
		}

		virtual void OnThreadLock(void)
		{
			iLog("Thread lock");
		}

		inline float GetLastUpdateDeltaTime(void)
		{
			float timeRet = mTimer->GetMicroseconds();
			return (timeRet > 1.0f) ? 1.0f : timeRet;
		}

		//private data
		bool Synchronization(bool ready)
		{
			Platform::HANDLE waitEvents[2] = {mReadyForProcessing, mReadyToSync};
			Platform::DWORD dwRes;

			dwRes = Platform::WaitForMultipleObjects(2, waitEvents, 0, INFINITE);

			switch(dwRes)
			{
			case WAIT_OBJECT_0 : 
				if(ready)
				{
					ReadyForProcessing();
				}
				return true;
			case WAIT_OBJECT_0 + 1 :
				return OnWaitToSync(ready);
			}

			return false;
		}

		virtual bool OnThreadUpdate(float/* dt*/, bool ready)
		{
			return Synchronization(ready);
		}

		void WaitToSync(void)
		{
			iLog("wait to sync");
			lock(this);
			Platform::SetEvent(mWaitToSync);
		}

		bool OnWaitToSync(bool ready)
		{
			if(mThreadStop)
			{
				OnThreadStop();
				WaitToSync();
				return false;
			}
			else
			{
				OnThreadLock();
				WaitToSync();
				return Synchronization(ready);
			}
		}

		void Update(float/* dt*/)
		{
			//do nothing
		}
	};

	//
	//
	//
	class TNonThread : public TObject, virtual public IThreadEvent
	{
		InterfaceInClass;
	public:
		TNonThread()
		{}

		inline void ThreadStart(void)
		{
			OnThreadStart();
		}

		virtual void OnThreadStart(void)
		{

		}

		virtual void OnThreadStop(void)
		{

		}

		inline void ThreadSyncExit(void)
		{
			OnThreadStop();
		}

		virtual bool OnThreadUpdate(float/* dt*/, bool/* ready*/)
		{
			return true;
		}

		inline void ThreadLock(void)
		{
			OnThreadLock();
		}

		virtual void OnThreadLock(void)
		{
		}

		inline void ThreadUnlock(void)
		{
		}

		virtual void ReadyForProcessing(void)
		{
		}

		void Update(float dt)
		{
			OnThreadUpdate(dt, true);
		}
	};
}